package com.jackrain.nea.fc.cp.adutils;

import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.util.TypeUtils;
import com.jackrain.nea.config.PropertiesConf;
import com.jackrain.nea.exception.NDSException;
import com.jackrain.nea.util.ApplicationContextHandle;
import lombok.extern.slf4j.Slf4j;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.*;
import java.util.Hashtable;

/**
 * Created by ZCY on 2017/12/1.
 * AD域新增用户
 */
@Slf4j
public class AdUser {
    private static DirContext ctx = null;
    private static String groupname;
    private static Hashtable<String, String> env = new Hashtable<String, String>();
    private static String template;
    private static String disorgurl;


    public static void ldapload() {
        try {
            PropertiesConf pconf = ApplicationContextHandle.getBean(PropertiesConf.class);
            String ldapurl = pconf.getProperty("ad.url");
            String adminName = pconf.getProperty("ad.ssl.admin");
            String adminpassword = pconf.getProperty("ad.ssl.password");
            groupname = pconf.getProperty("ad.upper.organization");
            disorgurl = pconf.getProperty("ad.disable.organization");
            template = pconf.getProperty("ad.path.template");
            String javaHome = System.getProperty("java.home");
            StringBuffer keystorebuf = new StringBuffer("");
            keystorebuf.append(System.getProperty("java.home"));
            keystorebuf.append(pconf.getProperty("ad.ssl.trustStore"));

            String storepassword = pconf.getProperty("ad.ssl.trustStorePassword");
            System.setProperty("javax.net.ssl.trustStore", keystorebuf.toString());
            System.setProperty("javax.net.ssl.trustStorePassword", storepassword);
            env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            env.put(Context.SECURITY_AUTHENTICATION, "simple");
            env.put(Context.SECURITY_PRINCIPAL, adminName);
            env.put(Context.SECURITY_CREDENTIALS, adminpassword);
            env.put(Context.SECURITY_PROTOCOL, "ssl");
            env.put(Context.PROVIDER_URL, ldapurl);
        } catch (Exception ef) {
            log.error(ef + "===获取配置文件失败===", ef);
            throw new NDSException("获取配置文件失败:" + ef, ef);
        }
        try {
            ctx = new InitialDirContext(env);
            log.info("通过连接LDAP进行认证");
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e + "==无法通过LDAP认证===", e);
            throw new NDSException("登录的用户名或密码错误，无法通过LDAP认证:" + e, e);
        }
    }

    public static boolean disableUser(JSONObject userobj, JSONObject disobj) {
        String displayName = disobj.getString("displayName");
        if (StringUtils.isEmpty(displayName)) {
            throw new NDSException("用户姓名Ename不能为空");
        }
        String sAMAccountName = userobj.getString("sAMAccountName");
        if (StringUtils.isEmpty(sAMAccountName)) {
            throw new NDSException("用户名name(用户工号)为空");
        }
//        String sAMAccountName = princiname;
        Attributes attributes = searchUser(sAMAccountName);
        if (attributes == null) {
            return false;
        } else {
            String oldorgname = TypeUtils.castToString(attributes.get("distinguishedName"));
            oldorgname = oldorgname.substring(oldorgname.indexOf(":") + 2, oldorgname.length());

            StringBuffer newbuf = new StringBuffer("");
            newbuf.append("CN=").append(sAMAccountName).append(",");
            newbuf.append(disorgurl);
            String neworg = newbuf.toString();

            if (!neworg.equals(oldorgname)) {
                Modifyorguser(oldorgname, neworg);
                disobj.put("userAccountControl", "514");
            }
            Modifyusername(neworg, displayName, disobj);

        }
        return true;
    }

    public static boolean Modifyuserattribute(JSONObject userattribute, String disorgurl) {
        Attributes atu = new BasicAttributes();
        for (String key : userattribute.keySet()) {
            String values = TypeUtils.castToString(userattribute.get(key));
            atu.put(key, values);
        }
        try {
            ctx.modifyAttributes(disorgurl, DirContext.REPLACE_ATTRIBUTE, atu);
            return true;
        } catch (Exception e) {
            log.error(e + "===属性修改失败===", e);
            throw new NDSException("属性修改失败" + e, e);
        }

    }

    public static boolean acceptUser(JSONObject userobj) {
        String cn = userobj.getString("cn");
        if (StringUtils.isEmpty(cn)) {
            throw new NDSException("用户名name(用户工号)为空");
        }
        String unicodePwd = userobj.getString("unicodePwd");
        String displayName = userobj.getString("displayName");
        if (StringUtils.isEmpty(displayName)) {
            throw new NDSException("用户姓名Ename为空");
        }
        String fatherName = userobj.getString("fatherName");
        StringBuffer fatherbuf = new StringBuffer(fatherName);
        fatherbuf.append(",").append(groupname);
        fatherName = fatherbuf.toString();
        String sAMAccountName = cn;
        Attributes attributes = searchUser(sAMAccountName);
        StringBuffer neworgbuf = new StringBuffer("CN=");
        neworgbuf.append(cn).append(",").append(fatherName);
        String neworg = neworgbuf.toString();
        if (attributes == null) {
            //新增用户
            userobj.put("fatherName", fatherName);
            JSONObject huser = new JSONObject(true);
            huser.putAll(userobj);
            huser.put("distinguishedName", neworg);
            adduser(huser);
        } else {
            String oldEname = TypeUtils.castToString(attributes.get("displayName"));
            oldEname = oldEname.substring(oldEname.indexOf(":") + 2, oldEname.length());
            String oldorgname = TypeUtils.castToString(attributes.get("distinguishedName"));
            oldorgname = oldorgname.substring(oldorgname.indexOf(":") + 2, oldorgname.length());
            if (!neworg.equals(oldorgname)) {
                Modifyorguser(oldorgname, neworg);
            }
            userobj.remove("cn");
            userobj.remove("unicodePwd");
            userobj.remove("fatherName");
//            userobj.put("accountExpires", "9223372036854775807");
            userobj.put("userAccountControl", "66048");
            Modifyusername(neworg, displayName, userobj);
        }
        return false;
    }

    private static void validatePwd(String pwd) {
        PropertiesConf pconf = ApplicationContextHandle.getBean(PropertiesConf.class);
        Integer pwdlength = TypeUtils.castToInt(pconf.getProperty("ad.password.length"));
        if (pwdlength == null) {
            pwdlength = 5;
        }
        if (StringUtils.isEmpty(pwd)) {
            log.error("密码不能为空");
            throw new NDSException("密码不能为空");
        }
        if (pwd.length() < pwdlength) {
            log.error("密码长度不能小于" + pwdlength);
            throw new NDSException("密码长度不能小于" + pwdlength);
        }
    }

    public static String modifyenablepwd(JSONObject userobj) {
        String cn = userobj.getString("cn");
        if (StringUtils.isEmpty(cn)) {
            throw new NDSException("用户名name(用户工号)为空");
        }

        String unicodePwd = userobj.getString("unicodePwd");
        validatePwd(unicodePwd);
        String sAMAccountName = userobj.getString("sAMAccountName");
        Attributes attributes = searchUser(sAMAccountName);
        if (attributes == null) {
            log.error("AD域中不存在该用户" + sAMAccountName);
            throw new NDSException("AD域中不存在该用户" + sAMAccountName);
        }
        String oldorgname = TypeUtils.castToString(attributes.get("distinguishedName"));
        oldorgname = oldorgname.substring(oldorgname.indexOf(":") + 2, oldorgname.length());

        try {
            ModificationItem[] mods = new ModificationItem[1];
            String newQuotedPassword = "\"" + unicodePwd + "\"";
            byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
            mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword));
//        mods[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("userAccountControl", "66048"));
            ctx.modifyAttributes(oldorgname, mods);
            log.info("用户密码修改成功");
            return oldorgname;
        } catch (Exception ex) {
            ex.printStackTrace();
            log.error("数据库中禁用用用户,AD中组织目录:" + oldorgname + "密码修改失败:" + ex, ex);
            throw new NDSException("数据库中禁用用用户,AD中组织目录:" + oldorgname + "密码修改失败:" + ex, ex);
        }

    }

    public static String modifypwd(JSONObject userobj) {
        String cn = userobj.getString("cn");
        if (StringUtils.isEmpty(cn)) {
            throw new NDSException("用户名name(用户工号)为空");
        }
        String unicodePwd = userobj.getString("unicodePwd");
        validatePwd(unicodePwd);
        String sAMAccountName = userobj.getString("sAMAccountName");
        Attributes attributes = searchUser(sAMAccountName);
        if (attributes == null) {
            log.error("AD域中不存在该用户" + sAMAccountName);
            throw new NDSException("AD域中不存在该用户" + sAMAccountName);
        }
        String oldorgname = TypeUtils.castToString(attributes.get("distinguishedName"));
        oldorgname = oldorgname.substring(oldorgname.indexOf(":") + 2, oldorgname.length());
        try {
            ModificationItem[] mods = new ModificationItem[2];
            String newQuotedPassword = "\"" + unicodePwd + "\"";
            byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
            mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword));
            mods[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("userAccountControl", "66048"));
            ctx.modifyAttributes(oldorgname, mods);

            log.info("用户密码修改成功");
            return oldorgname;
        } catch (Exception ex) {
            log.error("修改数据库中启用用户,AD中组织目录:" + oldorgname + "密码修改失败:" + ex, ex);
            throw new NDSException("修改数据库中启用用户,AD中组织目录:" + oldorgname + "密码修改失败:" + ex, ex);
        }

    }

    //修改用户名
    public static boolean Modifyusername(String org, String displayName, JSONObject userobj) {
        try {
            ctx.getAttributes(org);
        } catch (Exception e) {
            e.printStackTrace();
            throw new NDSException("该条目" + org + "已不存在:" + e, e);
        }
        Attributes attributes = new BasicAttributes();
        try {
            attributes.put("displayName", displayName);

            //TODO 新增性别
//            attributes.put("sex",userobj.getString("sex"));

            attributes.put("sn", displayName.substring(0, 1));
//            attributes.put("name", displayName);
            if (displayName.length() >= 2) {
                attributes.put("givenName", displayName.substring(1, displayName.length()));
            } else {
                attributes.put("givenName", null);
            }
            attributes.put("description", displayName + "  FC项目同步生成");
            updateAttribute(userobj, attributes);
            ctx.modifyAttributes(org, DirContext.REPLACE_ATTRIBUTE, attributes);
            return true;

        } catch (Exception e) {
//            e.printStackTrace();
            log.error("修改AD域用户名失败:" + e, e);
            throw new NDSException("修改AD域用户名失败:" + e, e);
        }

//        return false;
    }

    //修改用户的上级组织
    public static boolean Modifyorguser(String oldorg, String neworg) {
        try {
            ctx.getAttributes(oldorg);
            log.info("存在原来的组织");
        } catch (Exception e) {
            log.error("AD域中原有用户记录" + oldorg + "不存在:" + e, e);
            throw new NDSException("AD域中原有用户记录" + oldorg + "不存在:" + e, e);
        }
        try {
            ctx.rename(oldorg, neworg);
            return true;
        } catch (Exception el) {
            log.error("修改用户所属组织出错,新的上级组织还不存在:" + el, el);
            throw new NDSException("修改用户所属组织出错,新的上级组织还不存在:" + el, el);
        }
    }

    //查询是否已存在该用户是新增还是修改
    public static Attributes searchUser(String sAMAccountName) {

        String returnedAtts[] = {
                "cn", "sn", "givenname", "distinguishedName", "displayName",
                "userAccountControl", "name", "weixin","sex",
                "userPrincipalName", "mail", "mobile", "distinguishedName"
        };
        SearchControls searchCtls = new SearchControls();
        searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        searchCtls.setReturningAttributes(returnedAtts);

        StringBuffer filterbuf = new StringBuffer("(&");
        filterbuf.append("(sAMAccountName=").append(sAMAccountName);
        filterbuf.append(")").append("(objectClass=organizationalPerson)(objectClass=user)(!(objectClass=computer)))");
        //  定义条件
        String searchFilter = filterbuf.toString();
        String searchBase = groupname;
        Attributes attributes = null;
        try {
            //  执行查询
            NamingEnumeration<SearchResult> answer = ctx.search(searchBase, searchFilter, searchCtls);
            if (answer.hasMoreElements()) {
                SearchResult sr = (SearchResult) answer.next();
                attributes = sr.getAttributes();

            } else {
                log.info("AD域中不存在该用户");
                return attributes;
            }
        } catch (Exception e) {
            log.info("AD域中不能存在该用户");
            return null;

        }
        return attributes;

    }


    //新增用户
    public static boolean adduser(JSONObject userobj) {

        String cn = userobj.getString("cn");
        String displayName = userobj.getString("displayName");
        String unicodePwd = userobj.getString("unicodePwd");
        
        //TODO 新增性别
//        String sex=userobj.getString("sex");

        String fatherName = userobj.getString("fatherName");
        String distinguishedName = userobj.getString("distinguishedName");
        if (unicodePwd!=null) {
            validatePwd(unicodePwd);
        }
        try {
            ctx.getAttributes(fatherName);
        } catch (Exception e) {
            log.error("所属组织" + fatherName + "不存在:" + e, e);
            throw new NDSException("所属组织" + fatherName + "不存在:" + e, e);
        }
        Attributes attrsuser = null;
        try {
            attrsuser = ctx.getAttributes(distinguishedName);
            log.info(distinguishedName + "用户已存在");
            return false;
        } catch (Exception e) {
            log.info("准备新增用户");
        }

        if (attrsuser != null) {
            log.error(fatherName + "该组织中" + cn + "已存在");
            throw new NDSException(fatherName + "该组织中" + cn + "已存在");
        }
        Context result = null;
        try {
            BasicAttributes attrsbu = new BasicAttributes();
            BasicAttribute objclassSet = new BasicAttribute("objectclass");
            objclassSet.add("person");
            objclassSet.add("top");
            objclassSet.add("user");
            objclassSet.add("organizationalPerson");
            //创建Attributes，并添加objectclass和cn属性
            attrsbu.put(objclassSet);
            userobj.remove("fatherName");
            userobj.remove("unicodePwd");

            addAttribute(userobj, attrsbu);
            attrsbu.put("accountExpires", "9223372036854775807");
            attrsbu.put("description", displayName + "  FC项目同步生成");
            attrsbu.put("displayName", displayName);
            attrsbu.put("userPrincipalName", cn + template);
            attrsbu.put("sn", displayName.substring(0, 1));
            if (displayName.length() >= 2) {
                attrsbu.put("givenName", displayName.substring(1, displayName.length()));
            }
            attrsbu.put("sAMAccountName", cn);
            attrsbu.put("name", cn);
//            attrsbu.put("sex",sex);
//            attrsbu.put("name", cn);
//            int UF_ACCOUNTDISABLE = 0x0002;
            int UF_PASSWD_NOTREQD = 0x0020;
            int UF_NORMAL_ACCOUNT = 0x0200;
            int UF_DONT_EXPIRE_PASSWD = 0x10000;
            attrsbu.put("userAccountControl", Integer.toString(UF_PASSWD_NOTREQD + UF_NORMAL_ACCOUNT + UF_DONT_EXPIRE_PASSWD));
            result = ctx.createSubcontext(distinguishedName, attrsbu);
            if (StringUtils.isNotEmpty(unicodePwd)) {
                ModificationItem[] mods = new ModificationItem[2];
                String newQuotedPassword = "\"" + unicodePwd + "\"";
                byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
                mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword));
                mods[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("userAccountControl", "66048"));
                ctx.modifyAttributes(distinguishedName, mods);
            }

            log.info("新增用户成功");
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            if (result != null) {
                deleteUserLdap(distinguishedName);
                log.error("密码加密不成功,新增用户失败:" + ex, ex);
                throw new NDSException("密码加密不成功,新增用户失败:" + ex, ex);
            } else {
                log.error(cn + "该用户名在其他组织目录中已存在,新增用户失败: " + ex, ex);
                throw new NDSException(cn + "该用户名在其他组织目录中已存在,新增用户失败: " + ex, ex);
            }

        }

    }

    public static Attributes addAttribute(JSONObject userobj, Attributes attributes) {
        for (String key : userobj.keySet()) {
            String values = TypeUtils.castToString(userobj.get(key));
            if (StringUtils.isNotEmpty(values)) {
                attributes.put(key, values);
            }
        }
        return attributes;
    }

    public static Attributes updateAttribute(JSONObject userobj, Attributes attributes) {
        for (String key : userobj.keySet()) {
            String values = TypeUtils.castToString(userobj.get(key));
            attributes.put(key, values);
        }
        return attributes;
    }

    public static boolean deleteUserLdap(String account) {
        try {
            ctx.destroySubcontext(account);
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            log.error("删除用户失败: " + ex, ex);
            throw new NDSException("删除用户失败: " + ex, ex);
        }
    }

    // 关闭LDAP服务器连接

    public static void closeCtx() {
        try {
            ctx.close();
        } catch (Exception ex) {
            log.error("=====关闭LDAP连接失败: " + ex, ex);
        }
    }
}
